#include "execute.h"
#include "builtin.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>

#define WRFLAGS O_WRONLY | O_CREAT | O_TRUNC
#define RDFLAGS O_RDONLY
#define PERMS	S_IWUSR | S_IRUSR

/** funciones auxiliares **/
void exec_intern_command (scommand * scmd);
void exec_single_pipe (pipeline * pl);
void exec_double_pipe (pipeline * pl);
void scommand_format (scommand * scmd, char ** argv);
/** fin funciones auxiliares **/

void execute (pipeline * pl)
{
	assert (pl != NULL);
	if (pipeline_length (pl) == 1)
	{
		exec_single_pipe (pl);
	}
	else if (pipeline_length (pl) == 2)
	{
		exec_double_pipe (pl);
	}
}

void exec_single_pipe (pipeline * pl)
{
	const_bstring redir_in = NULL;
	const_bstring redir_out = NULL;
	char * redirin = NULL;
	char * redirout = NULL;
	scommand * scmd = NULL;
	char ** argv = NULL;
	int com_len = 0;
	pid_t fork_id = 0;
	bool esperar = false;		

	assert ( (pl!=NULL) && (pipeline_length (pl) == 1) );
	

	esperar = pipeline_get_wait (pl);
	scmd = pipeline_front (pl);
	com_len = scommand_length (scmd);

	if (scommand_length (scmd) > 0)
	{ 

		if (es_interno (scmd))
		{
			exec_intern_command (scmd);
		}
		else
		{

		argv = (char **) calloc (com_len, sizeof (char *));

		assert (argv != NULL);

		scommand_format (scmd, argv);
		redir_in = scommand_get_redir_in (scmd);
		redir_out = scommand_get_redir_out (scmd);
	
		fork_id = fork ();
		if (fork_id < 0)
		{
			perror ("fork ()");
			exit (EXIT_FAILURE);
		}
		else if (fork_id == 0) /** soy el hijo **/
		{
			if (redir_in != NULL)
			{
				int flags = O_RDONLY;
				int perm = S_IWUSR | S_IRUSR;
				int fid = 0;
				int dup_res = 0;			

				redirin = bstr2cstr (redir_in, '\0');
				fid = open (redirin, flags, perm);
				if (fid < 0)
				{
					perror ("open ()");
					exit (EXIT_FAILURE);
				}
				close (STDIN_FILENO);
				dup_res = dup(fid);
				if (dup_res < 0)
				{
					perror ("dup ()");
					exit (EXIT_FAILURE);
				}
				close (fid);
			}
			if (redir_out != NULL)
			{
				int flags = O_WRONLY | O_CREAT | O_TRUNC;
				int perm = S_IWUSR | S_IRUSR;
				int fid = 0;
				int dup_res = 0;

				redirout = bstr2cstr (redir_out, '\0');
				fid = open (redirout, flags, perm);
				if (fid < 0)
				{
					perror ("open ()");
					exit (EXIT_FAILURE);
				}
				close (STDOUT_FILENO);
				dup_res = dup (fid);
				if (dup_res < 0)
				{
					perror ("dup ()");
					exit (EXIT_FAILURE);
				}
				close (fid);
			}	

			execvp (argv[0], argv);
			perror ("execvp ()");
			exit (EXIT_FAILURE);
		}	
		else if (fork_id > 0) /** soy el padre **/
		{
			int status = 0;
			int s = 0;
			if (esperar)
			{
				s = waitpid (fork_id, &status, 0);
			}
		}
		}
	}
}

void exec_double_pipe (pipeline * pl)
{
	const_bstring redir_in1 = NULL, redir_out2 = NULL;
	char * redirin1 = NULL; 
	char * redirout2 = NULL;
	scommand * scmd1 = NULL;
	scommand * scmd2 = NULL;
	char ** argv1 = NULL;
	char ** argv2 = NULL;
	int com_len1 = 0, com_len2 = 0, pipe_res = 0;
	pid_t forkid1 = 0, forkid2 = 0;
	bool esperar = false;
	int pipe_fd[2] = {0,0};


	assert ( (pl != NULL) && (pipeline_length (pl) == 2) );

	esperar = pipeline_get_wait (pl);
	scmd1 = pipeline_front (pl);
	pipeline_pop_front (pl);
	scmd2 = pipeline_front (pl);
	com_len1 = scommand_length (scmd1);
	com_len2 = scommand_length (scmd2);
	argv1 = (char **) calloc (com_len1, sizeof (char *));
	argv2 = (char **) calloc (com_len2, sizeof (char *));

	assert (argv1 != NULL && argv2 != NULL);
	scommand_format (scmd1, argv1);
	scommand_format (scmd2, argv2);

	redir_in1 = scommand_get_redir_in (scmd1);
	redir_out2 = scommand_get_redir_out (scmd2);
	
	pipe_res = pipe (pipe_fd); /**creo el pipe **/
	if (pipe_res < 0)
	{
		perror ("pipe ()");
		exit (EXIT_FAILURE);
	}

	forkid1 = fork ();
	if (forkid1 < 0)
	{
		perror ("fork ()");
		exit (EXIT_FAILURE);
	}
	else if (forkid1 == 0) /** soy el hijo 1, escribo en el pipe**/
	{
		if (redir_in1 != NULL)
		{
			int fid = 0;
			redirin1 = bstr2cstr (redir_in1, '\0');
			fid = open (redirin1, RDFLAGS, PERMS);
			if (fid < 0)
			{
				perror ("open ()");
				exit (EXIT_FAILURE);
			}
			close (STDIN_FILENO);
			dup (fid);
			close (fid);
		}
		close (pipe_fd[0]); /**no leo, escribo **/
		close (STDOUT_FILENO);
		dup (pipe_fd[1]);
		close (pipe_fd[1]);
		execvp (argv1[0], argv1);
		perror ("execvp ()");
		exit (EXIT_FAILURE);
	}
	else if (forkid1 > 0) /** soy el padre **/
	{
		forkid2 = fork();
		if (forkid2 < 0)
		{
			perror ("fork ()");
			exit (EXIT_FAILURE);
		}
		else if (forkid2 == 0) /** soy el hijo 2, leo del pipe **/
		{
			if (redir_out2 != NULL)
			{
				int fid = 0;
				redirout2 = bstr2cstr (redir_out2, '\0');
				fid = open (redirout2, WRFLAGS, PERMS);
				if (fid < 0)
				{
					perror ("open ()");
					exit (EXIT_FAILURE);
				}
				close (STDOUT_FILENO);
				dup (fid);
				close (fid);
			}
			close (pipe_fd[1]); /** no escribo, leo **/
			close (STDIN_FILENO);
			dup (pipe_fd[0]);
			close (pipe_fd[0]);
			execvp (argv2[0], argv2);
			perror ("execvp ()");
			exit (EXIT_FAILURE);
		}
		else if (forkid2 > 0)
		{
			int status1 = 0, status2 = 0;
			close (pipe_fd[0]);
			close (pipe_fd[1]);
			waitpid (forkid1, &status1, 0);
			waitpid (forkid2, &status2, 0);
		}
	}
}

void exec_intern_command (scommand * scmd)
{
	const_bstring aux = NULL;
	bstring comando = NULL;
	int err = 0;

	aux =  scommand_front (scmd);
	comando = bstrcpy (aux); 
	if (biseqcstr (comando, "cd"))
	{
		char * path = NULL;
		if (scommand_length (scmd) == 1) /**cd sin arg **/
		{
			err = my_cd (NULL);
		}
		else
		{
			scommand_pop_front (scmd);
			/*bdestroy (aux);*/
			aux = scommand_front (scmd);
			path = bstr2cstr (aux, '\0');
			err = my_cd (path);
			bdestroy (comando);
		}
	}	
	else if (biseqcstr (comando, "exit"))
	{
		bdestroy (comando);
		salir ();
	}
}

void scommand_format (scommand * scmd, char ** argv)
{
	int i = 0, com_len = 0;
	const_bstring curr_arg = NULL;
	char * aux = NULL;
	assert ( (scmd != NULL) && (argv != NULL) );
	
	curr_arg = scommand_front (scmd);
	aux = bstr2cstr (curr_arg, '\0');
	argv[0] = aux;
	com_len = scommand_length (scmd);
	for (i=1; i < com_len; i++)
	{
		scommand_pop_front (scmd);
		curr_arg = scommand_front (scmd);
		aux = bstr2cstr (curr_arg, '\0');
		argv[i] = aux;
	}
	argv[i] = NULL;
}
	